﻿using System;
using System.Collections.Generic;
using System.Data;
using viNews.Classes;
using viNews.Providers;
using visinia.SmartEngine.Cache;
using visinia.SmartEngine.Pages;

namespace viNews.Controllers
{
    /// <summary>
    /// The main controller of Category operations
    /// </summary>
    public class CategoryController
    {
        #region CACHE KEYS
        private const string KEY_CATEGORY = "category_{0}";
        private const string KEY_CATEGORIES = "categories";        
        private const string KEY_CATEGORY_BY_TITLE_PARENTID = "categoryByTitle_ParentId_{0}_{1}";
        private const string KEY_SUB_CATEGORIES = "categorySubCategories_{0}";
        #endregion

        #region ACCESS
        /// <summary>
        /// Gets a Category by its name, to access child category set PerentCategoryId=0
        /// </summary>
        /// <param name="Name">Category Name</param>
        /// <param name="PerentCategoryId">Parent category id for child categories otherwise pass 0</param>
        /// <returns>returns the Category</returns>
        public static Category GetCategory(string Name, int PerentCategoryId)
        {
            string key = string.Format(KEY_CATEGORY_BY_TITLE_PARENTID, Name, PerentCategoryId);
            Category category = (Category)DataCache.GetCache(key);
            if (category == null)
            {
                string nameLowered = Name.ToLower();
                category = GetCategories().Find(p => ((p.Name.ToLower() == nameLowered) && (p.ParentId == PerentCategoryId)));
                DataCache.SetCache(key, category);
            }
            return category;
        }

        /// <summary>
        /// Returns a Specific Category
        /// </summary>
        /// <param name="CategoryId">CategoryId of the Category to Access</param>
        /// <returns>The Category whose CategoryId is given</returns>
        public static Category GetCategory(int CategoryId)
        {
            string key = string.Format(KEY_CATEGORY, CategoryId);
            Category cat = (Category)DataCache.GetCache(key);
            if (cat == null)
            {
                cat = GetCategories().Find(p => p.CategoryId == CategoryId);
                DataCache.SetCache(key, cat);
            }
            return cat;
        }

        /// <summary>
        /// Return all Categories
        /// </summary>
        /// <returns>List of all Categories</returns>
        public static List<Category> GetCategories()
        {
            string key = KEY_CATEGORIES;
            List<Category> cats = (List<Category>)DataCache.GetCache(key);
            if (cats == null)
            {
                cats = FillCategories(viNewsDataProvider.Provider.GetCategories());
                DataCache.SetCache(key, cats);
            }
            return cats;
        }

        /// <summary>
        /// Returns all parent categories
        /// </summary>
        /// <returns>List of parent catetories</returns>
        public static List<Category> GetParentCategories()
        {
            string key = "categoriesParents";
            List<Category> parents = (List<Category>)DataCache.GetCache(key);
            if (parents == null)
            {
                List<Category> list = GetCategories();
                parents = list.FindAll(p => p.ParentId < 1);
                DataCache.SetCache(key, parents);
            }
            return parents;
        }

        /// <summary>
        /// Retuens all subcategories of a specified parent
        /// </summary>
        /// <param name="ParentId">The parent category</param>
        /// <returns>List of subcategories</returns>
        public static List<Category> GetCategories(int ParentId)
        {
            string key = string.Format(KEY_SUB_CATEGORIES, ParentId);
            List<Category> subcategories = (List<Category>)DataCache.GetCache(key);
            if (subcategories == null)
            {
                subcategories = GetCategories().FindAll(p => p.ParentId == ParentId);
                DataCache.SetCache(key, subcategories);
            }
            return subcategories;
        }

        /// <summary>
        /// Returns a category's parent category 
        /// </summary>
        /// <param name="CategoryId">subcategory whose parent to return</param>
        /// <returns>Parent catetory of the category</returns>
        public static Category GetParentCategory(int CategoryId)
        {
            Category category = GetCategories().Find(p => p.CategoryId == CategoryId);
            if (category != null)
            {
                if (category.ParentId == 0)
                    return null;
                else
                    return GetCategory(category.ParentId);
            }
            return null;
        }

        /// <summary>
        /// Returns a category's parent category id
        /// </summary>
        /// <param name="CategoryId">subcategory whose parent id to return</param>
        /// <returns>Catetory id of the parent category</returns>
        public static int GetParentCategoryId(int CategoryId)
        {
            Category category = GetCategories().Find(p => p.CategoryId == CategoryId);
            if (category != null)
                return category.ParentId;
            return 0;
        }
        #endregion

        #region DATA
        /// <summary>
        /// Adds a new Category & Saves it to DataStore
        /// </summary>
        /// <param name="category">The Category to be added </param>
        public static void AddCategory(Category category)
        {
            viNewsDataProvider.Provider.AddCategory(category.Name, category.ParentId, category.PageId, category.ContentPageId);
            DataCache.DeleteCacheMultiple("cat");
        }

        /// <summary>
        /// Updates the category
        /// </summary>
        /// <param name="category"></param>
        public static void UpdateCategory(Category category)
        {
            viNewsDataProvider.Provider.UpdateCategory(category.CategoryId, category.Name, category.ParentId, category.PageId, category.ContentPageId);
            DataCache.DeleteCacheMultiple("cat");
        }

        /// <summary>
        /// Deletes the Category
        /// </summary>
        /// <param name="CategoryId">Id of the Category to Delete</param>
        public static void DeleteCategory(int CategoryId)
        {
            Category mainCategory = CategoryController.GetCategory(CategoryId);
            List<Category> childCategories = GetCategories(CategoryId);
            foreach (Category category in childCategories)
            {
                PageInfo page = PageController.GetPage(category.PageId);
                if (page != null)
                    PageController.DeletePage(page);
                PageInfo contentPage = PageController.GetPage(category.ContentPageId);
                if (contentPage != null)
                    PageController.DeletePage(contentPage);
                viNewsDataProvider.Provider.DeleteCategory(category.CategoryId);
            }
            PageInfo mainPage = PageController.GetPage(mainCategory.PageId);
            if (mainPage != null)
                PageController.DeletePage(mainPage);
            PageInfo mainContentPage = PageController.GetPage(mainCategory.ContentPageId);
            if (mainContentPage != null)
                PageController.DeletePage(mainContentPage);
            viNewsDataProvider.Provider.DeleteCategory(CategoryId);
            DataCache.DeleteCacheMultiple("cat");
        }

        /// <summary>
        /// Deletes the Category
        /// </summary>
        /// <param name="Category">The Category to Delete</param>
        public static void DeleteCategory(Category category)
        {
            DeleteCategory(category.CategoryId);
        }
        #endregion

        #region HELPERS
        private static Category FillCategory(IDataReader dr)
        {
            while (dr.Read())
            {
                return ActualRead(dr, true);
            }
            return null;
        }
        private static List<Category> FillCategories(IDataReader dr)
        {
            List<Category> list = new List<Category>();
            while (dr.Read())
            {
                list.Add(ActualRead(dr, false));
            }
            dr.Close();
            return list;
        }
        private static Category ActualRead(IDataReader dr, bool CloseDataReader)
        {
            Category cat = new Category();
            if (dr["CategoryId"] != Convert.DBNull)
                cat.CategoryId = (int)dr["CategoryId"];
            if (dr["ParentId"] != Convert.DBNull)
                cat.ParentId = (int)dr["ParentId"];
            if (dr["Name"] != Convert.DBNull)
                cat.Name = (string)dr["Name"];
            if (dr["PageId"] != Convert.DBNull)
                cat.PageId = (int)dr["PageId"];
            if (dr["ContentPageId"] != Convert.DBNull)
                cat.ContentPageId = (int)dr["ContentPageId"];
            if(CloseDataReader)
                dr.Close();
            return cat;
        }
        #endregion
    }
}
